package com.tx.websocket.demo.shiro;

import com.tx.websocket.demo.redis.CommonConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisSessionDao extends EnterpriseCacheSessionDAO {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private CommonConfig commonConfig;

    //redis namespace
    private static final String REDIS_SESSION_NAMESPACE = "shiro-session:";
    private static final String REDIS_SESSION_CACHE = "shiro-cache:";

    private static final Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        session.setTimeout(commonConfig.getSessionTimeout() * 60 * 1000);
        redisTemplate.boundValueOps(REDIS_SESSION_NAMESPACE + sessionId).set(session, commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
            logger.info("sessionId {} 被创建", sessionId);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        Object sessionVal = redisTemplate.opsForValue().get(REDIS_SESSION_NAMESPACE + sessionId);
        if(sessionVal != null) {
            logger.debug(" session {} ttl is {}", sessionId, redisTemplate.getExpire(REDIS_SESSION_NAMESPACE + sessionId));
            redisTemplate.boundValueOps(REDIS_SESSION_NAMESPACE + sessionId).expire(commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
            logger.debug("sessionId {} 被读取, 重置过期时间", sessionId);
        }
        return (Session)sessionVal;
    }

    @Override
    protected void doUpdate(Session session) {
        if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
            logger.debug("session {} 失效或者校验失败", session.getId());
            return;
        }
        if (session instanceof SimpleSession) {

            redisTemplate.boundValueOps(REDIS_SESSION_NAMESPACE + session.getId()).set(session, commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
            logger.debug("sessionId {} 被更新", session.getId());
            return;
        }

        if (session instanceof Serializable) {
            redisTemplate.boundValueOps(REDIS_SESSION_NAMESPACE + session.getId()).set(session, commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
            logger.debug("serializable sessionId {} 被更新", session.getId());
        }
    }

    @Override
    protected void doDelete(Session session) {
        //删除session
        redisTemplate.delete(REDIS_SESSION_NAMESPACE + session.getId());
        //删除cache
        redisTemplate.delete(REDIS_SESSION_CACHE + session.getId());
        logger.debug("session {} 被清除" + session.getId());
    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<String> keys = redisTemplate.keys("*" + REDIS_SESSION_NAMESPACE + "*");
        if (CollectionUtils.isEmpty(keys)) {
            return null;
        }
        List<Object> valueList = redisTemplate.opsForValue().multiGet(keys);
        List<Session> sessions = new ArrayList<>();
        for(Object o : valueList)
            sessions.add((Session) o);
        return sessions;
    }
}
